; SLIME 20100404
user> (use clojure.contrib.def)
; Evaluation aborted.
user> (class clojure)
; Evaluation aborted.
user> (use clojure.xml)
; Evaluation aborted.
user> (use 'clojure.contrib.def)
nil
user> (ns my.snake)
nil
my.snake> (import (java.awt Color Dimension)
		  (javax.swing JPanel JFrame Timer JOptionPane)
		  (java.awt.event ActionListener KeyListener))
java.awt.event.KeyListener
my.snake> (use 'clojure.contrib.import-static
	       '[clojure.contrib.seq-utils :only (includes?)])
nil
my.snake> 
nil
my.snake> (def width 75)
#'my.snake/width
my.snake> (def height 50)
#'my.snake/height
my.snake> (def point-size 10)
#'my.snake/point-size
my.snake> (def turn-millis 75)
#'my.snake/turn-millis
my.snake> (def win-length 5)
#'my.snake/win-length
my.snake> 
; Evaluation aborted.
my.snake> VK_LEFT
; Evaluation aborted.
my.snake> (import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
#'my.snake/VK_RIGHT
my.snake> (def dirs { VK_LEFT [-1 0]
	       VK_RIGHT [1 0]
	       VK_UP [0 -1]
	       VK_DOWN [0 1]})
#'my.snake/dirs
my.snake> 
; Evaluation aborted.
my.snake> (defn add-points [& pts]
		(vec (apply map + pts)))
#'my.snake/add-points
my.snake> (defn point-to-screen-rect [pt]
	    (map #(* point-size %) [(pt 0) (pt 1) 1 1]))
#'my.snake/point-to-screen-rect
my.snake> (add-points [10 10] [-1 0])
[9 10]
my.snake> (defn create-apple []
	    {:location [(rand-int width) (rand-int height)]
	    :color (Color. 210 50 90)
	    :type :apple}) 
#'my.snake/create-apple
my.snake> (defn create-snake []
	    {:body (list [1 1])
	    :dir [1 0]
	    :type :snake 
	    :color (Color. 15 160 70)})
#'my.snake/create-snake
my.snake> (list [1 1])
([1 1])
my.snake> (defn move [{:keys [body dir] :as snake} & grow]
		(assoc snake :body (cons (add-points (first body) dir)
					 (if grow body (butlast body)))))
#'my.snake/move
my.snake> (move (create-snake))
{:body ([2 1]), :dir [1 0], :type :snake, :color #<Color java.awt.Color[r=15,g=160,b=70]>}
my.snake> (move (create-snake))
{:body ([2 1]), :dir [1 0], :type :snake, :color #<Color java.awt.Color[r=15,g=160,b=70]>}
my.snake> (move (create-snake) :grow)
{:body ([2 1] [1 1]), :dir [1 0], :type :snake, :color #<Color java.awt.Color[r=15,g=160,b=70]>}
my.snake> (defn win? [{body :body}]
		(>= (count body) win-length))
#'my.snake/win?
my.snake> (win? {:body [[1 1]]})
false
my.snake> (win? {:body [[1 1] [1 2] [1 3] [1 4] [1 5]]})
true
my.snake> (defn head-overlaps-body? [{[head & body] :body}]
		(includes? body head))
#'my.snake/head-overlaps-body?
my.snake> (defn lose? head-overlaps-body?)
; Evaluation aborted.
my.snake> (def lose? head-overlaps-body?)
#'my.snake/lose?
my.snake> 
; Evaluation aborted.
my.snake> (lose? {:body [[1 2] [1 2] [1 3] [1 2]]})
true
my.snake> (defn eats? [{[snake-head] :body} {apple :location}]
		(= snake-head apple))
#'my.snake/eats?
my.snake> (eats? {:body [[1 1] [1 2]]} {:location [2 2]})
false
my.snake> (eats? {:body [[1 1] [1 2]]} {:location [1 2]})
false
my.snake> (eats? {:body [[1 1] [1 2]]} {:location [1 1]})
true
my.snake> (defn trun [snake newdir]
		(assoc snake :dir newdir))
#'my.snake/trun
my.snake> (defn turn [snake newdir]
		(assoc snake :dir newdir))
#'my.snake/turn
my.snake> (def trun nil)
#'my.snake/trun
my.snake> (turn (create-snake) [0 -1])
{:body ([1 1]), :dir [0 -1], :type :snake, :color #<Color java.awt.Color[r=15,g=160,b=70]>}
my.snake> (defn reset-game [snake apple]
		(dosync (ref-set apple (create-apple))
			(ref-set snake (create-snake)))
		nil)
			
#'my.snake/reset-game
my.snake> (reset-game test-snake test-apple)
; Evaluation aborted.
my.snake> (def test-snake (ref nil)
	    )
#'my.snake/test-snake
my.snake> (def test-apple (ref nil))
#'my.snake/test-apple
my.snake> (reset-game test-snake test-apple)
nil
my.snake> @test-apple
{:location [53 3], :color #<Color java.awt.Color[r=210,g=50,b=90]>, :type :apple}
my.snake> @test-snake
{:body ([1 1]), :dir [1 0], :type :snake, :color #<Color java.awt.Color[r=15,g=160,b=70]>}
my.snake> (defn update-direction [snake newdir]
		(when newdir (dosync (alter snake turn newdir))))
#'my.snake/update-direction
my.snake> (update-direction test-snake [0 -1])
{:body ([1 1]), :dir [0 -1], :type :snake, :color #<Color java.awt.Color[r=15,g=160,b=70]>}
my.snake> 
#'my.snake/update-positions
my.snake> (defn update-positions [snake apple]
		(dosync
		 (if (eats? @snake @apple)
		     (do (ref-set apple (create-apple))
			 (alter snake move :grow))
		     (alter snake move)))
		nil)
#'my.snake/update-positions
my.snake> (reset-game test-snake test-apple)
nil
my.snake> (dosync (alter test-apple assoc :location [1 1]))
{:location [1 1], :color #<Color java.awt.Color[r=210,g=50,b=90]>, :type :apple}
my.snake> (update-positions test-snake test-apple)
nil
my.snake> (:body @test-snake)
([2 1] [1 1])
my.snake> (defn fill-point [g pt color]
		(let [[x y width height] (point-to-screen-rect pt)]
		     (.setColor g color)
		     (.fillRect g x y width height)))
#'my.snake/fill-point
my.snake> (defmulti paint (fn [g object & _] (:type object))) 
#'my.snake/paint
my.snake> (defmethod paint :apple [g {:keys [location color]}]
		     (fill-point g location color))
#<MultiFn clojure.lang.MultiFn@19bf2ac>
my.snake> (defmethod paint :snake [g {:keys [body color]}]
		     (doseq [point body]
			    (fill-point g point color)))
#<MultiFn clojure.lang.MultiFn@19bf2ac>
my.snake> (defn game-panel [frame snake apple] 
		(proxy [JPanel ActionListener KeyListener] []
		       (paintComponent [g]
				       (proxy-super paintComponent g)
				       (paint g @snake)
				       (paint g @apple))
		       (actionPerformed [e]
					(update-positions snake apple)
					(when (lose? @snake)
					  (reset-game snake apple)
					  (JOptionPane/showMessageDialog frame "You lose!"))
					(when (win? @snake)
					  (reset-game snake apple)
					  (JOptionPane/showMessageDialog frame "You win!"))
					(.repaint this))
		       (keyPressed [e]
				   (update-direction snake (dirs (.getKeyCode e))))
		       (getPreferredSize []
					 (Dimension. (* (inc width) point-size)
						     (* (inc height) point-size)))
		       (keyReleased [e])
		       (keyTyped [e])))
#'my.snake/game-panel
my.snake> dirs
{37 [-1 0], 39 [1 0], 38 [0 -1], 40 [0 1]}
my.snake> (defn game []
	    (let [snake (ref (create-snake))
		 apple (ref (create-apple))
		 frame (JFrame. "Snake")
		 panel (game-panel frame snake apple)
		 timer (Timer. turn-millis panel)]
		 (doto panel
		       (.setFocusable true)
		       (.addKeyListener panel))
		 (doto frame
		       (.add panel)
		       (.pack)
		       (.setVisible true))
		 (.start timer)
		 [snake, apple, timer])) 
#'my.snake/game
my.snake> (game)
[#<Ref@1d18ac9: {:body ([1 1]), :dir [1 0], :type :snake, :color #<Color java.awt.Color[r=15,g=160,b=70]>}> #<Ref@1eec137: {:location [49 23], :color #<Color java.awt.Color[r=210,g=50,b=90]>, :type :apple}> #<Timer javax.swing.Timer@1cf0f2e>]
my.snake> (in-ns 'user)




